package cosc455.project1;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * The character by character parser for the the recursive-decent syntax
 * analyzer. It builds strings through a char array with a limit of 140
 * characters. Implements LexicalAnalyzerInterface.
 * 
 * @author BinhDang
 */
public class LexicalAnalyzer implements LexicalAnalyzerInterface {

	// A string containing all the text from the file
	private String sourceLine;

	// The next character
	private char nextCharacter;

	// Char array used to make a string
	private char[] charBuilder = new char[140];

	private int charLength;
	private int currentPosition;
	private String newToken;

	// A String ArrayList holding all the valid terminals
	private ArrayList<String> list;

	// Flag when the source is empty
	private boolean end = false;

	/**
	 * Initiates ArrayList of valid terminals and first character.
	 * 
	 * @param line
	 *            String being parsed
	 */
	public void start(String line) {
		sourceLine = line;
		currentPosition = 0;
		list = new ArrayList<String>();
		list.add("#HAI");
		list.add("#KTHXBYE");
		list.add("#OBTW");
		list.add("#TLDR");
		list.add("#MAEK HEAD");
		list.add("#OIC");
		list.add("#GIMMEH TITLE");
		list.add("#MKAY");
		list.add("#MAEK PARAGRAF");
		list.add("#GIMMEH BOLD");
		list.add("#GIMMEH ITALICS");
		list.add("#MAEK LIST");
		list.add("#GIMMEH ITEM");
		list.add("#GIMMEH NEWLINE");
		list.add("#GIMMEH SOUNDZ");
		list.add("#GIMMEH VIDZ");
		list.add("#I HAS A");
		list.add("#ITZ");
		list.add("#VISIBLE");

		getCharacter();
		getNextToken();
	}

	@Override
	public void getNextToken() {
		charLength = 0;

		// Ignore spaces and add the first character to the token
		getNonBlank();
		addCharacter();
		getCharacter();

		// Continue gathering characters for the token
		while ((nextCharacter != '\n') && (nextCharacter != ' ')
				&& (nextCharacter != '\f') && (nextCharacter != '\r')
				&& (nextCharacter != '\t')) {
			addCharacter();
			getCharacter();
		}

		// Convert the gathered character array token into a String
		newToken = new String(charBuilder);

		// converts token to Upper case if and only if it is an annotation
		if (isAnnotate(newToken)) {
			newToken = newToken.toUpperCase();

		}

		Compiler.currentToken = newToken.substring(0, charLength);

		if (Compiler.currentToken.equals("#GIMMEH")
				|| Compiler.currentToken.equals("#MAEK")
				|| Compiler.currentToken.equals("#I")) {
			// System.out.println("Test Double Annotation");
			addCharacter();
			getCharacter();

			while ((nextCharacter != '\n') && (nextCharacter != ' ')
					&& (nextCharacter != '\t') && (nextCharacter != '\r')
					&& (nextCharacter != '\f')) {
				addCharacter();
				getCharacter();
			}

			newToken = new String(charBuilder);
			newToken = newToken.toUpperCase();
			Compiler.currentToken = newToken.substring(0, charLength);

			if (Compiler.currentToken.equals("#I HAS")) {
				addCharacter();
				getCharacter();

				while ((nextCharacter != '\n') && (nextCharacter != ' ')) {
					addCharacter();
					getCharacter();
				}

				newToken = new String(charBuilder);
				newToken = newToken.toUpperCase();
				Compiler.currentToken = newToken.substring(0, charLength);
			}

		}

		if (isAnnotate(Compiler.currentToken)) {
			lookupToken();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cosc455.project1.LexicalAnalyzerInterface#getCharacter()
	 */
	public void getCharacter() {
		if (currentPosition < sourceLine.length()) {
			nextCharacter = sourceLine.charAt(currentPosition++);
		} else {
			nextCharacter = '\n';
			endParser();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cosc455.project1.LexicalAnalyzerInterface#addCharacter()
	 */
	@Override
	public void addCharacter() {
		if (charLength <= 138) {
			charBuilder[charLength++] = nextCharacter;
			charBuilder[charLength] = 0;
		} else {
			System.out
					.println("LEXICAL ERROR - token  exceeds the 140 character limit becuz twitterz");

			if (!isSpace(nextCharacter)) {
				while (!isSpace(nextCharacter)) {
					getCharacter();
				}
			}
			charLength = 0;
			getNonBlank();
			addCharacter();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cosc455.project1.LexicalAnalyzerInterface#isSpace(char)
	 */
	@Override
	public boolean isSpace(char c) {
		if ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\f')
				|| (c == '\r') || (c == '\u000B')) {
			return true;
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cosc455.project1.LexicalAnalyzerInterface#lookupToken()
	 */
	@Override
	public boolean lookupToken() {
		if (list.contains(Compiler.currentToken)) {
			return true;
		} else {
			SyntaxAnalyzer.setError();
			System.err.println("ERROR: Lexical Error.\nAnnotatation "
					+ Compiler.currentToken + " is not legal");
			return false;
		}
	}

	/**
	 * Skips special whitespace character i.e. '\n' '\t'
	 */
	private void getNonBlank() {
		while (isSpace(nextCharacter) && end != true) {
			getCharacter();
		}
	}

	/**
	 * @param s
	 *            String token being pass
	 * @return true if the token is an annotation tag; else return false
	 */
	public static boolean isAnnotate(String s) {
		if (s.charAt(0) == '#')
			return true;
		return false;
	}

	/**
	 * Sets a flag marker when there are no characters left to be pass; ends the
	 * parser
	 */
	public void endParser() {
		end = true;
	}

}
